﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.Ex.DataDictionaryValueDescription
{
    /// <summary>
    /// 表类型的数据字典
    /// </summary>
    public class TypeDataDictionary
    {
        private string _des;
        /// <summary>
        /// 数据字典描述说明
        /// </summary>
        public string Des
        {
            get { return _des; }
        }

        /// <summary>
        /// [key:字段值;value:字段值对应的含义值名称和值]
        /// </summary>
        private readonly Dictionary<object, NameValue<Object>> _fieldMeanDic = new Dictionary<object, NameValue<object>>();

        /// <summary>
        /// [key:字段值对应的含义值;value:字段值]
        /// </summary>
        private readonly Dictionary<object, object> _meanFieldDic = new Dictionary<object, object>();



        /// <summary>
        /// 构造函数
        /// </summary>
        public TypeDataDictionary(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }


#if NET4_0
            var desAtt = UtilZ.Dotnet.Ex.Base.TypeEx.GetCustomAttribute<DescriptionAttribute>(type, true);
#else
            var desAtt = type.GetCustomAttribute<DescriptionAttribute>();
#endif
            if (desAtt != null)
            {
                this._des = desAtt.Description;
            }
            else
            {
                this._des = type.FullName;
            }

            Type dataDictionaryItemAttributeType = typeof(DataDictionaryItemAttribute);
            FieldInfo[] fieldInfos = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            DataDictionaryItemAttribute att;
            object fieldValue;

            foreach (var fieldInfo in fieldInfos)
            {
#if NET4_0
                att = UtilZ.Dotnet.Ex.Base.TypeEx.GetCustomAttribute<DataDictionaryItemAttribute>(fieldInfo, false);
#else
                att = (DataDictionaryItemAttribute)fieldInfo.GetCustomAttribute(dataDictionaryItemAttributeType, false);
#endif
                if (att == null)
                {
                    continue;
                }

                fieldValue = fieldInfo.GetValue(null);
                this.PrimitiveAdd(fieldValue, att.Value, att.Name);
            }
        }


        private void PrimitiveAdd(object fieldValue, object meanValue, string name)
        {
            if (this._fieldMeanDic.ContainsKey(fieldValue))
            {
                throw new ArgumentException($"\"{this._des}\"字段值\"{fieldValue}\"已存在");
            }

            if (this._meanFieldDic.ContainsKey(meanValue))
            {
                throw new ArgumentException($"\"{this._des}\"含义值\"{fieldValue}\"已存在");
            }

            this._fieldMeanDic.Add(fieldValue, new NameValue<object>(name, meanValue));
            this._meanFieldDic.Add(meanValue, fieldValue);
        }


        /// <summary>
        /// 添加一项数据字典
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <param name="meanValue">含义值</param>
        /// <param name="name">字段值名称</param>
        /// <exception cref="ArgumentNullException">字段值或含义值为null将抛出异常</exception>
        public void Add(object fieldValue, object meanValue, string name)
        {
            if (fieldValue == null)
            {
                throw new ArgumentNullException(nameof(fieldValue));
            }

            if (meanValue == null)
            {
                throw new ArgumentNullException(nameof(meanValue));
            }

            this.PrimitiveAdd(fieldValue, meanValue, name);
        }

        /// <summary>
        /// 添加一项数据字典
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <param name="meanValue">含义值</param>
        /// <param name="name">字段值名称</param>
        /// <exception cref="ArgumentNullException">字段值或含义值为null将抛出异常</exception>
        public void Update(object fieldValue, object meanValue, string name)
        {
            if (fieldValue == null)
            {
                throw new ArgumentNullException(nameof(fieldValue));
            }

            if (meanValue == null)
            {
                throw new ArgumentNullException(nameof(meanValue));
            }

            this.RemoveByFieldValue(fieldValue);
            this.RemoveByMeanValue(meanValue);
            this.Add(fieldValue, meanValue, name);
        }

        /// <summary>
        /// 根据字段值移除数据字典项
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <exception cref="ArgumentNullException">字段值为null将抛出异常</exception>
        public void RemoveByFieldValue(object fieldValue)
        {
            if (fieldValue == null)
            {
                throw new ArgumentNullException(nameof(fieldValue));
            }

            object meanValue;
            if (this.TryGetMeanValue(fieldValue, out meanValue))
            {
                this._fieldMeanDic.Remove(fieldValue);
                this._meanFieldDic.Remove(meanValue);
            }
        }

        /// <summary>
        /// 根据含义值移除数据字典项
        /// </summary>
        /// <param name="meanValue">含义值</param>
        /// <exception cref="ArgumentNullException">含义值为null将抛出异常</exception>
        public void RemoveByMeanValue(object meanValue)
        {
            if (meanValue == null)
            {
                throw new ArgumentNullException(nameof(meanValue));
            }

            object fieldValue;
            if (this.TryGetFieldValue(meanValue, out fieldValue))
            {
                this._fieldMeanDic.Remove(fieldValue);
                this._meanFieldDic.Remove(meanValue);
            }
        }


        /// <summary>
        /// 清空数据字典
        /// </summary>
        public void Clear()
        {
            this._fieldMeanDic.Clear();
            this._meanFieldDic.Clear();
        }


        /// <summary>
        /// 根据字段值获取含义值
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <returns>含义值</returns>
        /// <exception cref="ArgumentException">字段值对应的项不存在,将抛出异常</exception>
        public object GetMeanValue(object fieldValue)
        {
            object meanValue;
            if (this.TryGetMeanValue(fieldValue, out meanValue))
            {
                return meanValue;
            }

            throw new ArgumentException($"\"{this._des}\"字段值\"{fieldValue}\"对应的含义值不存在");
        }

        /// <summary>
        /// 根据字段值尝试获取含义值[获取成功返回true,失败返回false]
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <param name="meanValue">含义值</param>
        /// <returns>[获取成功返回true,失败返回false]</returns>
        /// <exception cref="ArgumentNullException">字段值为null抛出异常</exception>
        public bool TryGetMeanValue(object fieldValue, out object meanValue)
        {
            if (fieldValue == null)
            {
                throw new ArgumentNullException(nameof(fieldValue));
            }

            NameValue<Object> item;
            if (this._fieldMeanDic.TryGetValue(fieldValue, out item))
            {
                meanValue = item.Value;
                return true;
            }

            meanValue = null;
            return false;
        }




        /// <summary>
        /// 根据含义值获取字段值
        /// </summary>
        /// <param name="meanValue"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public object GetFieldValue(object meanValue)
        {
            object fieldValue;
            if (this.TryGetFieldValue(meanValue, out fieldValue))
            {
                return fieldValue;
            }

            throw new ArgumentException($"\"{this._des}\"含义值\"{meanValue}\"对应的字段值不存在");
        }

        /// <summary>
        /// 根据含义值尝试获取字段值[获取成功返回true,失败返回false]
        /// </summary>
        /// <param name="meanValue">含义值</param>
        /// <param name="fieldValue">字段值</param>
        /// <returns>[获取成功返回true,失败返回false]</returns>
        /// <exception cref="ArgumentNullException">含义值为null抛出异常</exception>
        public bool TryGetFieldValue(object meanValue, out object fieldValue)
        {
            if (meanValue == null)
            {
                throw new ArgumentNullException(nameof(meanValue));
            }

            return this._meanFieldDic.TryGetValue(meanValue, out fieldValue);
        }




        /// <summary>
        /// 获取字段值名称
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <returns>字段值名称</returns>
        /// <exception cref="ArgumentException">字段值对应的项不存在,将抛出异常</exception>
        public string GetFieldValueName(object fieldValue)
        {
            string name;
            if (this.TryGetFieldValueName(fieldValue, out name))
            {
                return name;
            }

            throw new ArgumentException($"\"{this._des}\"字段值\"{fieldValue}\"对应的项不存在");
        }

        /// <summary>
        /// 根据字段值尝试获取名称[获取成功返回true,失败返回false]
        /// </summary>
        /// <param name="fieldValue">字段值</param>
        /// <param name="name">名称</param>
        /// <returns>[获取成功返回true,失败返回false]</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public bool TryGetFieldValueName(object fieldValue, out string name)
        {
            if (fieldValue == null)
            {
                throw new ArgumentNullException(nameof(fieldValue));
            }

            NameValue<Object> item;
            if (this._fieldMeanDic.TryGetValue(fieldValue, out item))
            {
                name = item.Name;
                return true;
            }

            name = null;
            return false;
        }



        /// <summary>
        /// 获取字段值名称
        /// </summary>
        /// <param name="meanValue">含义值</param>
        /// <returns>字段值名称</returns>
        /// <exception cref="ArgumentException">字段值对应的项不存在,将抛出异常</exception>
        public string GetMeanValueName(object meanValue)
        {
            string name;
            if (this.TryGetMeanValueName(meanValue, out name))
            {
                return name;
            }

            throw new ArgumentException($"\"{this._des}\"含义值\"{meanValue}\"对应的项不存在");
        }

        /// <summary>
        /// 根据字段值尝试获取名称[获取成功返回true,失败返回false]
        /// </summary>
        /// <param name="meanValue">含义值</param>
        /// <param name="name">名称</param>
        /// <returns>[获取成功返回true,失败返回false]</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public bool TryGetMeanValueName(object meanValue, out string name)
        {
            if (meanValue == null)
            {
                throw new ArgumentNullException(nameof(meanValue));
            }

            object fieldValue;
            if (this._meanFieldDic.TryGetValue(meanValue, out fieldValue))
            {
                return this.TryGetFieldValueName(fieldValue, out name);
            }

            name = null;
            return false;
        }
    }
}
